#include "control.h"

void press_key(BYTE key, bool is_control, bool is_meta, bool is_shift)
{
    if (is_control)
        keybd_event(VK_CONTROL, (BYTE)MapVirtualKey(VK_CONTROL, 0), 0, 0);
    if (is_meta)
        keybd_event(VK_MENU, (BYTE)MapVirtualKey(VK_MENU, 0), 0, 0);
    if (is_shift)
        keybd_event(VK_SHIFT, (BYTE)MapVirtualKey(VK_SHIFT, 0), 0, 0);

    keybd_event(key, (BYTE)MapVirtualKey(key, 0), 0, 0);
    keybd_event(key, (BYTE)MapVirtualKey(key, 0), KEYEVENTF_KEYUP, 0);

    if (is_shift)
        keybd_event(VK_SHIFT, (BYTE)MapVirtualKey(VK_SHIFT, 0), KEYEVENTF_KEYUP, 0);
    if (is_meta)
        keybd_event(VK_MENU, (BYTE)MapVirtualKey(VK_MENU, 0), KEYEVENTF_KEYUP, 0);
    if (is_control)
        keybd_event(VK_CONTROL, (BYTE)MapVirtualKey(VK_CONTROL, 0), KEYEVENTF_KEYUP, 0);
}

void keybd_event_ex(BYTE key, bool is_press)
{
    INPUT input;
    memset(&input, 0, sizeof(INPUT));
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = key;
    input.ki.wScan = (WORD)MapVirtualKey((UINT)key, 0);
    if (is_press)
        input.ki.dwFlags=0;
    else
        input.ki.dwFlags=KEYEVENTF_KEYUP;
    logf("<< %u >>", SendInput(1, &input, sizeof(INPUT)));
}

void press_key_ex(BYTE key)
{
    keybd_event_ex(key, true);
    Sleep(50);
    keybd_event_ex(key, false);

}

bool check_window(const wchar_t *__class_name, const wchar_t *__window_name)
{
    wchar_t window_name[100], class_name[100];
    HWND hwd_curr = GetForegroundWindow();
    GetWindowText(hwd_curr, window_name, 100);
    GetClassName(hwd_curr, class_name, 100);
    
    //MessageBox(NULL, window_name, class_name, MB_OK);
    return ((wcslen(__class_name) == 0  || wcsstr(class_name,  __class_name)) &&
            (wcslen(__window_name) == 0 || wcsstr(window_name, __window_name)));
}

bool in_ppt()
{
    return check_window(L"PPTFrameClass", L"Microsoft PowerPoint");
}

bool in_play_ppt()
{
    return check_window(L"screenClass", L"PowerPoint 幻灯片放映");
}

bool in_explorer()
{
    return check_window(L"CabinetWClass", L"") || 
           check_window(L"", L"Program Manager") ||
           check_window(L"FolderView", L"");
}

bool in_picture()
{
    return check_window(L"Photo_Lightweight_Viewer", L"Windows 照片查看器");
}

bool in_media()
{
    return check_window(L"WMPlayerApp", L"") ||
           check_window(L"TTPlayer_PlayerWnd", L"") ||
           check_window(L"{E7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}", L"") ||
           check_window(L"kwmusicmaindlg", L"") ||
           check_window(L"XLUEFrameHostWnd", L"") ||
           check_window(L"MediaPlayerClassicW", L"") ||
           check_window(L"Baofeng_StormPlayer", L"") || 
           check_window(L"PotPlayer", L"") ||
           check_window(L"Winamp", L"");
}

bool in_browser()
{
    return check_window(L"IEFrame", L"Windows Internet Explorer") ||
           check_window(L"MozillaWindowClass", L"");
}

bool in_taiko()
{
    return check_window(L"Windows", L"太鼓次郎");
}

bool command(char flag, int x, int y)
{
    static double last_command = 1.0;
#ifdef EX_TAIKO
    if (flag >= 'a' && flag <= 'l')
    {
        if (((double)clock() / CLOCKS_PER_SEC  - last_command) < MINIMAL_GAP_GAME)
            return false;
        last_command = (double)clock() / CLOCKS_PER_SEC;
        if (flag == 'c')
            last_command += 0.3;
    }
#else
    if (flag != 'm' && flag != 'o')
    {
        if (((double)clock() / CLOCKS_PER_SEC  - last_command) < MINIMAL_GAP)
            return false;
        last_command = (double)clock() / CLOCKS_PER_SEC;
    }
#endif
    switch (flag) {
    case 'o':
        // DO NOTHING
        return false;

    case '<':
        // Left move
        
        if (in_browser()) {
            logf(">> Control.cpp: Ctrl-Shift-Tab is pressed\n");
            press_key(VK_TAB, true, false, true);
        } else {
            logf(">> Control.cpp: Left is pressed\n");
            press_key(VK_LEFT, false, false, false);
        }
        break;

    case '>':
        // Right move
        if (in_browser()) {
            logf(">> Control.cpp: Ctrl-Tab is pressed\n");
            press_key(VK_TAB, true, false, false);
        }
        else {
            logf(">> Control.cpp: Right is pressed\n");
            press_key(VK_RIGHT, false, false, false);
        }
        break;

    case ',':
        logf(">> Control.cpp: Clap mode entered\n");
        if (in_taiko())
            return false;
        break;

    case '+':
        if (in_media()) {
                logf(">> Control.cpp: VK_MEDIA_NEXT_TRACK\n");
                press_key(VK_MEDIA_NEXT_TRACK, false, false, false);
        } else {
                logf(">> Control.cpp: Ctrl-+ is pressed\n");
                press_key(VK_OEM_PLUS, true, false, false);
                press_key(VK_OEM_PLUS, true, false, false);
        }
        break;

    case '-':
        if (in_media()) {
                logf(">> Control.cpp: VK_MEDIA_NEXT_TRACK\n");
                press_key(VK_MEDIA_PREV_TRACK, false, false, false);
        } else {
                logf(">> Control.cpp: Ctrl-- is pressed\n");
                press_key(VK_OEM_MINUS, true, false, false);
                press_key(VK_OEM_MINUS, true, false, false);
        }
        break;

    case '.':
        // Enter / Exit command
        if (in_play_ppt()) {
            logf(">> Control.cpp: We are playing PowerPoint, ESC is pressed\n");
            press_key(VK_ESCAPE, false, false, false);
        } else if (in_ppt()) {
            logf(">> Control.cpp: We are waiting for PowerPoint, F5 is pressed\n");
            press_key(VK_F5, false, false, false);
        } else if (in_picture()) {
            logf(">> Control.cpp: We are in some specified application, \
                 Meta-F4 is pressed\n");
            press_key(VK_F4, false, true, false);
        } else if (in_browser()) {
            logf(">> Control.cpp: We are in some specified application, \
                 Ctrl-F4 is pressed\n");
            press_key(VK_F4, true, false, false);
        } else if (in_media()) {
            logf(">> Control.cpp: Play/Pause");
            press_key(VK_MEDIA_PLAY_PAUSE, false, false, false);
        } else if (in_taiko()) {
            logf(">> Control.cpp: taiki mode: <Space> is pressed");
            press_key(VK_SPACE, false, false, false);
        } else {
            //logf(">> Control.cpp: We are in File Manager, Enter is pressed\n");
            press_key(VK_RETURN, false, false, false);
        }
        break;

    case 'm':
        // Drag move
        logf(">> Control.cpp: Drag (%d, %d)\n", x, y);

        if (in_taiko())
            return false;
        else if (in_picture()) {
            logf(">> Control.cpp: In drag mode\n");
            BYTE dirx = (x < 0 ? VK_RIGHT : VK_LEFT);
            BYTE diry = (y < 0 ? VK_UP    : VK_DOWN);
            for (int i = 0; i < abs(x); ++i)
                press_key(dirx, false, true, false);
            for (int j = 0; j < abs(y); ++j)
                press_key(diry, false, true, false);
        } else if (in_browser()) {
                press_key(VK_ESCAPE, false, false, false);
                press_key(VK_ESCAPE, false, false, false);
            logf(">> Control.cpp: In drag mode\n");
            BYTE dirx = (x < 0 ? VK_RIGHT : VK_LEFT);
            BYTE diry = (y < 0 ? VK_UP    : VK_DOWN);
            for (int i = 0; i < abs(x) / 2; ++i)
                press_key(dirx, false, false, false);
            for (int j = 0; j < abs(y) / 2; ++j)
                press_key(diry, false, false, false);
        } else if (in_media()) {
            BYTE dirx = (x > 0 ? VK_VOLUME_UP    : VK_VOLUME_DOWN);
            for (int j = 0; j < (abs(x) / 2); ++j)
                press_key(dirx, false, false, false);
        } else if (in_taiko()) {
            BYTE diry = (y < 0 ? VK_DOWN  : VK_UP);
            for (int j = 0; j < abs(y) / 2; ++j)
                press_key(diry, false, false, false);
        } else {
            //logf(">> Control.cpp: In explorer mode\n");
            BYTE dirx = (x > 0 ? VK_RIGHT : VK_LEFT);
            BYTE diry = (y > 0 ? VK_UP    : VK_DOWN);
            for (int i = 0; i < (abs(x) > 3); ++i)
                press_key(dirx, false, false, false);
            for (int j = 0; j < (abs(y) > 2); ++j)
                press_key(diry, false, false, false);
        }
        break;

    case 'a':
        if (in_taiko())
            press_key_ex('Z');
        return true;

    case 'b':
        if (in_taiko())
            press_key_ex('A');
        return true;

    case 'c':
        if (in_taiko()) {
            clock_t start = clock();
            while (double(clock() - start) < .4 * CLOCKS_PER_SEC) {
                press_key_ex('Z');
                press_key_ex('/');
            }
        }
        return true;
    }
#ifdef EX_TAIKO
    return false;
#endif
    return true;
}

void change_result(result_t* r)
{
    clock_t now = clock();

    r->flag_ui = '\0';
    if (r->status + 1E-9 > 1.)
    {
        if ((r->flag_next == '.' || r->flag_next == 'm') && r->flag_prev != ',')
            r->status = 0.;

        if (r->status + 1E-9 > 1. &&
            command(r->flag_next, 0, 0))
        {
            r->flag_ui = r->flag_next;
            if (r->flag_next == '<' || r->flag_next == '>' ||
                (r->flag_next >= 'a' && r->flag_next <= 'l'))
                r->flag = 'o';
            else if (r->flag_next == '+' || r->flag_next == '-' ||
                 r->flag_next == ',')
                r->flag = ',';
            else if (r->flag_next == '.')
                r->flag = 'o';
            r->begin = now;
            r->status = 0.;
           
            r->flag_prev = r->flag_next;
        }
        else
            r->status = 0.;
    } else
    {

        if (r->flag == ',' && r->status > 0.2)
            r->begin = now;
        if (r->flag == 'm' && (r->dx != 0 || r->dy != 0))
        {
            command('m', r->dx, r->dy);
            r->begin = now;
        }

        if (r->flag == ',' && clock() - r->begin > CV_ZOOM_PERIOD)
        {
            if (r->flag_prev == ',')
            {
                r->flag = 'm';
                r->flag_next = 'm';
            } else
            {
                r->flag = 'o';
                r->flag_next = 'o';
            }
            r->flag_prev = r->flag_next;
            r->begin = now;
        }

        if (r->flag == 'm' && now - r->begin > CV_DRAG_PERIOD)
        {
            r->flag = 'o';
            r->flag_next = 'o';
            r->begin = now;
        }
    }
    //logf("(%c)", r->flag_prev);
}
